﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SerializableDictionary.cs" company="ScRePt">
//   Copyright © ScRePt 2010 - 2012
// </copyright>
// <summary>
//   The serializable dictionary.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MP3D.Helper
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;

    #endregion

    /// <summary>
    /// The serializable dictionary.
    /// </summary>
    /// <typeparam name="TKey">
    /// </typeparam>
    /// <typeparam name="TValue">
    /// </typeparam>
    [XmlRoot("dictionary")]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        #region Implemented Interfaces

        #region IXmlSerializable

        /// <summary>
        /// The get schema.
        /// </summary>
        /// <returns>
        /// </returns>
        public XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// The read xml.
        /// </summary>
        /// <param name="reader">
        /// The reader.
        /// </param>
        public void ReadXml(XmlReader reader)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            bool wasEmpty = reader.IsEmptyElement;

            reader.Read();

            if (wasEmpty)
            {
                return;
            }

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                TKey key;
                TValue value = default(TValue);
                bool iskeyAnAttribute = this.IsTypeAnAttribute(typeof(TKey));
                bool isvalueAnAttribute = this.IsTypeAnAttribute(typeof(TValue));

                if (isvalueAnAttribute)
                {
                    value = (TValue)Convert.ChangeType(reader.GetAttribute("value"), typeof(TValue));
                }

                if (iskeyAnAttribute)
                {
                    key = (TKey)Convert.ChangeType(reader.GetAttribute("key"), typeof(TKey));
                    reader.ReadStartElement("item");
                }
                else
                {
                    reader.ReadStartElement("item");
                    reader.ReadStartElement("key");
                    key = (TKey)keySerializer.Deserialize(reader);
                    reader.ReadEndElement();

                    if (!isvalueAnAttribute)
                    {
                        reader.ReadStartElement("value");
                    }
                }

                if (!isvalueAnAttribute)
                {
                    value = (TValue)valueSerializer.Deserialize(reader);
                }

                if (iskeyAnAttribute && !isvalueAnAttribute)
                {
                    reader.ReadEndElement();
                }

                if (key != null && !this.ContainsKey(key))
                {
                    this.Add(key, value);
                }

                if (!iskeyAnAttribute && !isvalueAnAttribute)
                {
                    reader.ReadEndElement();
                }

                reader.MoveToContent();
            }

            reader.ReadEndElement();
        }

        /// <summary>
        /// The write xml.
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        public void WriteXml(XmlWriter writer)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

            foreach (TKey key in this.Keys)
            {
                writer.WriteStartElement("item");

                if (this.IsTypeAnAttribute(typeof(TKey)))
                {
                    writer.WriteAttributeString("key", key.ToString());
                }
                else
                {
                    writer.WriteStartElement("key");
                    keySerializer.Serialize(writer, key);
                    writer.WriteEndElement();

                    if (!this.IsTypeAnAttribute(typeof(TValue)))
                    {
                        writer.WriteStartElement("value");
                    }
                }

                TValue value = this[key];

                if (this.IsTypeAnAttribute(typeof(TValue)))
                {
                    writer.WriteAttributeString("value", value.ToString());
                }
                else
                {
                    valueSerializer.Serialize(writer, value);
                }

                if (!this.IsTypeAnAttribute(typeof(TKey)) && ! this.IsTypeAnAttribute(typeof(TValue)))
                {
                    writer.WriteEndElement();
                }

                writer.WriteEndElement();
            }
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// The is type an attribute.
        /// </summary>
        /// <param name="thetype">
        /// The thetype.
        /// </param>
        /// <returns>
        /// The is type an attribute.
        /// </returns>
        private bool IsTypeAnAttribute(Type thetype)
        {
            return thetype.IsPrimitive || thetype.IsAssignableFrom(typeof(string));
        }

        #endregion
    }
}